package com.yuanmei.datas;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import com.yuanmei.datas.FetchKLineDaemon.KLineListener;
import com.yuanmei.model.StockItem;
import com.yuanmei.model.TickQuote;
import com.yuanmei.common.Utils;

import io.realm.Realm;
import io.realm.RealmConfiguration;

public class FetchDataService extends Service {
    private static final String TAG = FetchDataService.class.getSimpleName();
    private List<StockItem> mFetchList;
    private List<MarketListener> mMarketListenerList = new CopyOnWriteArrayList<>();
    private Thread mMainTimerThread;
//    private Thread mMinuteTickTimerThread;
    private boolean mTimerThreadRunning = true;
    private boolean mMainTimerThreadSleeping = false;
    private boolean mMinuteTickTimerThreadSleeping = false;
    private boolean mRealTimeQuoteFetchListUpdated = false;
    private boolean mMinuteTickQuoteFetchListUpdated = false;
    private boolean mRunMarkFetchOnce = true;
    private boolean mRunTickFlushOnce = true;
    //    private static final Object sUpdateRealTimeQuotesLock = new Object();
//    private static final Object sUpdateTickQuotesLock = new Object();
    private FetchKLineDaemon mFetchKLineDaemon = null;
    private Context mContext;

    public enum EmKLineType {
        DAY_LINE,
        WEEK_LINE,
        HOUR_LINE,
        MONTH_LINE,
    }

    // 市场行情数据监听接口
    public interface MarketListener {
        void onTodayQuotesFetched(ArrayList<String> quotesList);

        void onTodayMinuteTickQuoteFetched(String neCode);
    }

    private class MinuteTickTimerThread extends Thread {
        public void run() {
            ArrayList<StockItem> stockList = new ArrayList<>();
            while (mTimerThreadRunning) {
                try {
                    if (Utils.currentTimeIsTrading() || mRunTickFlushOnce) {
                        stockList.clear();
                        long tm1 = System.currentTimeMillis();
                        stockList.addAll(mFetchList);
                        mMinuteTickQuoteFetchListUpdated = false;
                        for (StockItem item : stockList) {
                            List<TickQuote> tickQuotes =
                                    NetEaseWebSource.replaceTodayMinuteTickData(
                                            item.netEaseCode(), Utils.getLastTradeDate());
                            if (tickQuotes == null || tickQuotes.size() <= 0) {
                                Log.w(TAG, item.name() + item.code() +
                                        "'s today minute tick data updated fail");
                                continue;
                            }
                            for (MarketListener listener : mMarketListenerList) {
                                listener.onTodayMinuteTickQuoteFetched(item.netEaseCode());
                            }
                            if (mMinuteTickQuoteFetchListUpdated) {
                                break;
                            }
                        }
                        long tm2 = System.currentTimeMillis();
                        Log.i(TAG, (mMinuteTickQuoteFetchListUpdated ? "Break and update " : "Update ") + stockList.size()
                                + " stock's tick data use " + (tm2 - tm1) + "ms");
                    }
                    mRunTickFlushOnce = false;
                    if (!mMinuteTickQuoteFetchListUpdated) {
                        mMinuteTickTimerThreadSleeping = true;
                        Thread.sleep(60 * 1000);
                        mMinuteTickTimerThreadSleeping = false;
                    }
                } catch (InterruptedException e) {
                    Log.i(TAG, "Tick Update thread is be interrupted");
                }
            }
        }
    }

    enum TodaySource {
        TenCent, NetEase
    }

    public FetchDataService() {
        super();
    }

    private static TodaySource sTodayQuotesSource;
    private static final String SP_DATA_SERVICE = "DATA_SERVICE";
    private static final String TODAY_QUOTE_SRC_KEY = "TODAY_QUOTE_SRC";

    SharedPreferences mDataServiceSp;

    public static ArrayList<String> fetchTodayQuotes(List<String> neCodeList, Realm realm) {
        switch (sTodayQuotesSource) {
            case NetEase:
                return NetEaseWebSource.fetchTodayQuotes(neCodeList, realm);
            case TenCent:
                return TenCentWebSource.fetchTodayQuotes(neCodeList, realm);
        }
        return null;
    }

    public class FetchDataBinder extends Binder {
        public void initMarketFetch(List<StockItem> newList,
                                    MarketListener initListener) {
            mFetchList = newList;
            mMarketListenerList.add(initListener);
            mMainTimerThread = new Thread(() -> {
//                mMinuteTickTimerThread = new MinuteTickTimerThread();
//                mMinuteTickTimerThread.start();
                ArrayList<String> notifyList = new ArrayList<>();
                ArrayList<StockItem> currFetchList = new ArrayList<>();
                RealmConfiguration todayRealmConfig = new RealmConfiguration.Builder()
                        .name("today_quotes.realm")
                        .deleteRealmIfMigrationNeeded()
                        .build();
                while (mTimerThreadRunning) {
                    try {
                        if (Utils.currentTimeIsTrading() || mRunMarkFetchOnce) {
                            long tm1 = System.currentTimeMillis();
                            notifyList.clear();
                            currFetchList.clear();
                            currFetchList.addAll(mFetchList);
                            mRealTimeQuoteFetchListUpdated = false;
                            Realm realm = Realm.getInstance(todayRealmConfig);
                            int bat = 50;
                            ArrayList<String> stockList = new ArrayList<>();
                            for (int i = 0; i < (currFetchList.size() + bat - 1) / bat; i++) {
                                List<StockItem> stockItemList =
                                        currFetchList.subList(i * bat,
                                                Math.min((i + 1) * bat,
                                                        currFetchList.size()));
                                stockList.clear();
                                for (StockItem item : stockItemList) {
                                    stockList.add(item.netEaseCode());
                                }
//                                ArrayList<String> resultList = NetEaseWebSource.fetchTodayQuotes(stockList, realm);
                                ArrayList<String> resultList = fetchTodayQuotes(stockList, realm);
                                if (resultList != null) {
                                    notifyList.addAll(resultList);
                                }
                                if (mRealTimeQuoteFetchListUpdated) {
                                    Log.i(TAG, "Fetch list updated, break fist loop");
                                    break;
                                }
                            }
                            realm.close();
                            if (!Realm.compactRealm(todayRealmConfig)) {
                                Log.w(TAG, "After NetEase update today real-time quotes, compact realm fail");
                            }
                            if (notifyList.size() > 0) {
                                for (MarketListener listener : mMarketListenerList) {
                                    listener.onTodayQuotesFetched(notifyList);
                                }
                                mRunMarkFetchOnce = false;
                            }
                            long tm2 = System.currentTimeMillis();
                            Log.i(TAG, (mRealTimeQuoteFetchListUpdated ? "Break and update " : "Update ") + notifyList.size() +
                                    " stock's current price use " + (tm2 - tm1) + "ms, notifyCount=" + notifyList.size() +
                                    "; DataSource=" + sTodayQuotesSource.name());
                        }
                        if (!mRealTimeQuoteFetchListUpdated) {
                            mMainTimerThreadSleeping = true;
                            Thread.sleep(5 * 1000);
                            mMainTimerThreadSleeping = false;
                        }
                    } catch (InterruptedException e) {
                        Log.i(TAG, "Monitor thread is be interrupted");
                    }
                }
            });
            mRunMarkFetchOnce = true;
            mMainTimerThread.start();

            mFetchKLineDaemon.scheduleNormalUpdate(mFetchList);
        }

        public void resetFetchList(List<StockItem> newList) {
            mFetchList = newList;
            mRunMarkFetchOnce = true;
            mRunTickFlushOnce = true;

            mRealTimeQuoteFetchListUpdated = true;
            if (mMainTimerThreadSleeping) {
                mMainTimerThread.interrupt();
            }

            mMinuteTickQuoteFetchListUpdated = true;
//            if (mMinuteTickTimerThreadSleeping) {
//                mMinuteTickTimerThread.interrupt();
//            }

            mFetchKLineDaemon.scheduleNormalUpdate(mFetchList);
        }

        public void flushStockData() {
            mFetchKLineDaemon.scheduleNormalUpdate(mFetchList);
        }

        public void startPriorUpdateHistory(String neCode,
                                            boolean isIndex,
                                            EmKLineType type,
                                            KLineListener listener) {
            mFetchKLineDaemon.startPriorUpdate(neCode, isIndex, type, listener);
        }

        public void registerMarketListener(MarketListener ml) {
            mMarketListenerList.add(ml);
        }

        public void unregisterMarketListener(MarketListener ml) {
            mMarketListenerList.remove(ml);
        }

        public void toggleTodayQuoteSource() {
            if (sTodayQuotesSource.ordinal() >= TodaySource.values().length - 1) {
                sTodayQuotesSource = TodaySource.values()[0];
            } else {
                sTodayQuotesSource = TodaySource.values()[sTodayQuotesSource.ordinal() + 1];
            }
            SharedPreferences.Editor editor = mDataServiceSp.edit();
            editor.putInt(TODAY_QUOTE_SRC_KEY, sTodayQuotesSource.ordinal());
            editor.apply();
        }

        public String getTodayQuoteSourceName() {
            String full_name = "源:";
            switch (sTodayQuotesSource) {
                case NetEase:
                    full_name += "网易";
                    break;
                case TenCent:
                    full_name  += "腾讯";
                    break;
            }
            return full_name;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "FetchDataService onBind");
        return new FetchDataBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mFetchKLineDaemon = new FetchKLineDaemon();
        mContext = this;
        mDataServiceSp = mContext.getSharedPreferences(SP_DATA_SERVICE, MODE_PRIVATE);
        int order = mDataServiceSp.getInt(TODAY_QUOTE_SRC_KEY, 0);
        sTodayQuotesSource = TodaySource.values()[ order < TodaySource.values().length ? order : 0];
    }

    @Override
    public void onDestroy() {
        mTimerThreadRunning = false;
        mMainTimerThread.interrupt();
        mFetchKLineDaemon.stop();
        mFetchKLineDaemon = null;
        super.onDestroy();
    }

}